package dse.modules;

import java.util.*;

//import testing.TestHelper;

import dse.*;
//import dse.modules.eln.EarthLikeNetwork;

public class GeneralRequestEventScheduler implements RequestEventScheduler {
	private Random random;
	private int requestFrequency; // requests per node per second
	private ZipfDistribution zipfDist;
	private int svrFrequency;
	
	public void initalize (
			ExperimentConfiguration systemSettings, 
			Map<String, String> moduleSettings
			)
	throws DseException
	{
		random = systemSettings.getRandom();
		
		ModuleUtil.SettingsReader sr = ModuleUtil.getSettingsReader(
				this.getClass(), moduleSettings
				);
		requestFrequency =  sr.getInteger("request-frequency");
		svrFrequency = sr.getInteger("svr-frequency");
		int zipfDistCard = sr.getInteger("zipf-dist-cardinality");
		zipfDist = new ZipfDistribution(0.7,zipfDistCard);
		// Adjust for time period differences
		//requestFrequency = systemSettings.getTimePeriodLength()*requestFrequency;
	}
	
	public Map<Node, DataObject> getRequests (
			Network network,
			Map<Cluster, DataObjectAccessRanking> accessRankings
			)
	{
		// Continue as normal
		TreeMap<Node, DataObject> requests = 
			new TreeMap<Node, DataObject>();
		
		
		// Figure out how many nodes to generate requests for...
		double nd = random.nextDouble();
		int rc = (int)(
				nd * requestFrequency * network.nodeCount() * 2
				);
		
		for (int a = 0; a < rc; a++) {
			int n = random.nextInt(network.nodeCount());
			Node node = network.lookupNode(n+1);
			if (node.state() != NodeState.Up)
				continue;
			
			int randomRank = zipfDist.getRank(random.nextDouble());
			
			// See what do that rank corresponds to...
			int cid = node.clusterId();
			DataObjectAccessRanking rankings = 
				accessRankings.get(network.lookupCluster(cid));
			
			DataObject dobj = rankings.get(randomRank);
			
			requests.put(node, dobj);
		}
		
		return requests;
	}
	
	public TreeMap<Double, Pair<Node, DataObject>> getRequests (
			Experiment experiment,
			Map<Cluster, DataObjectAccessRanking> accessRankings
			)
	{		
		// Continue as normal
		TreeMap<Double, Pair<Node, DataObject>> requests = 
			new TreeMap<Double, Pair<Node, DataObject>>();
		Network network = experiment.network();
		
		// Figure out how many nodes to generate requests for...
		//double nd = random.nextDouble();
		int rc = requestFrequency;//(int)(nd * requestFrequency * network.nodeCount() * 2);
		//rc = (int)requestFrequency * network.nodeCount();
		double curTime = experiment.currentTime();
		double startTime = 0;
		double timePeriodLength = experiment.configuration().getTimePeriodLength();
		for (int a = 0; a < rc; a++)
		{
			int n = random.nextInt(network.nodeCount());//(int) Math.random()*network.nodeCount();//
			Node node = network.lookupNode(n+1);
			//System.out.println("Node: " + node.id());
			if (node.state() != NodeState.Up)
				continue;
			
			DataObject dobj = null;
			if(experiment.dataObjects().size() > 1){
			
				int randomRank = zipfDist.getRank(random.nextDouble());
			
				// See what do that rank corresponds to...
				int cid = node.clusterId();
				DataObjectAccessRanking rankings = accessRankings.get(network.lookupCluster(cid));
			
				dobj = rankings.get(randomRank);//experiment.dataObjects().get(0);//5574);//
			}
			else
				dobj = experiment.dataObjects().get(0);
			requests.put(curTime + startTime, new Pair<Node, DataObject>(node, dobj));
			
			if (startTime < timePeriodLength)
			{	
				double pr = random.nextDouble();
				double deltaT = Math.log(pr)/(-rc)*timePeriodLength;
				startTime += deltaT;
			
				if (startTime > timePeriodLength)
					startTime = timePeriodLength;
			}
		}
		return requests;
	}
	
	public TreeMap<Node, DataObject> getSVRRequests (Experiment experiment){
		// Continue as normal
		int period = experiment.getCurrentPeriod();
		if(period%svrFrequency != 0)
			return null;
		
		TreeMap<Node, DataObject> requests = new TreeMap<Node, DataObject>();
		Network network = experiment.network();
		Collection<Node> nodes = network.nodes();
		for(Node node : nodes){
			LinkedList<DataObject> init = node.getSVRInitiator();
			if(init != null){
				for(DataObject dobj : init){
					DataShare ds = node.getDataShare(dobj);
					if(ds.getVersionSize() > 1){
						requests.put(node, dobj);
					}
				}
			}
		}
		return requests;
	}
	
	/**
	 * For testing :)
	 * 
	 * @param args
	 * @throws Exception
	 */
	/*public static void main (String [] args) 
	throws Exception
	{
		if (args.length != 6 && args.length != 7) {
			System.out.println(
					"Usage: java -cp ... " +
					"dse.modules.GeneralRequestEventScheduler " +
					"<nodes> <max-cluster-size> <num-data-objs> <diff> " +
					"<request-frequency> <rounds> (seed)"
					);
			System.exit(-1);
		}
		
		
		ExperimentConfiguration ss = TestHelper.createSystemSettings(args, 6);
		
		EarthLikeNetwork network = TestHelper.createELNetwork(
				ss, args, 0, -1, -1
				);
		
		TestHelper.createClusters(ss, args, 1, network);
		
		Vector<DataObject> dataObjects = TestHelper.createDataObjects(
				ss, args, 2, -1, -1, -1, -1
				);
		
		Map<Cluster,DataObjectAccessRanking> rankings = 
			TestHelper.generateDataObjectAccessRankings(
				ss, args, 3, network, dataObjects
				);
		
		GeneralRequestEventScheduler scheduler = 
			new GeneralRequestEventScheduler ();
		
		Hashtable<String, String> props = new Hashtable<String, String>();
		props.put("request-frequency", args[4]);
		props.put("zipf-dist-cardinality", args[2]);
		
		scheduler.initalize(ss, props);
		
		for (Node n : network.nodes()) n.setState(NodeState.Up);
		
		int rounds = Integer.parseInt(args[5]);
		
		for (int a = 0; a < rounds; a++) {
			System.out.println("Round #:" + (a+1));
			
			Map<Node, DataObject> requests = 
				scheduler.getRequests(network, rankings);
			
			for (Node n : requests.keySet()) {
				System.out.println(
						"Node " + n.id() + " (in " + n.clusterId() + 
						")\t" + requests.get(n).id()
						);
			}
		}
		
		System.out.println("Done.");
	}*/
}
